package com.yanqu.road.utils.redis;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.yanqu.road.utils.MyProperties;
import com.yanqu.road.utils.string.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import redis.clients.jedis.*;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class JedisHelper {
    private static Logger LOG = LogManager.getLogger(JedisHelper.class);

    private JedisPoolHelper pool;

    private static final String DEFAULT_HOST = "127.0.0.1";

    private static final int DEFAULT_PORT = 6379;

    private static JedisHelper instance;

    private static String redisPath = "";

    public static JedisHelper getInstance(String tempRedisPath) {
        redisPath = tempRedisPath;
        if (instance == null) {
            instance = new JedisHelper(redisPath);
        }
        return instance;
    }

    private JedisHelper(String redisPath) {
        init(redisPath);
    }

    private void init(String redisPath) {
        MyProperties prop = new MyProperties(redisPath);
        JedisPoolConfig config = initConfig(prop);
        String host = prop.getProperty("host", DEFAULT_HOST);
        int port = prop.getInt("port", DEFAULT_PORT);
        String requirepass = prop.getProperty("requirepass");

        pool = new JedisPoolHelper(config, host, port, Protocol.DEFAULT_TIMEOUT, StringUtils.isNullOrEmpty(requirepass) ? null : requirepass);

    }

    public void reInit() {
        if (pool != null) {
            pool.destroy();

            init(redisPath);
        }
    }

    /**
     * 判断redis是否需要重连操作
     *
     * @return
     */
    public boolean isNeedReconnect() {
        if (pool == null || pool.isClosed())
            return true;
        try {
            // 连接数量
            int activeNum = pool.getNumActive();
            if (activeNum < 0)
                return true;
            if (activeNum >= 50)
                return false;
        } catch (Exception e) {
            return true;
        }
        if (pool.getPoolFailCount() > 20)
            return true;

        return false;
    }

    /**
     * 判断redis是否重连成功
     *
     * @return
     */
    public boolean isSuccReconnect() {
        if (pool == null || pool.isClosed())
            return false;
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
        } catch (Exception e) {
            return false;
        } finally {
            close(jedis);
        }

        if (pool.getPoolFailCount() > 0)
            return false;

        return true;
    }

    private JedisPoolConfig initConfig(MyProperties prop) {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(prop.getInt("maxTotal", 50));
        config.setMaxIdle(prop.getInt("maxIdle", 10));
        config.setMinIdle(prop.getInt("minIdle", 1));
        config.setMaxWaitMillis(prop.getInt("maxWaitTime", 1000));

        return config;
    }

    public void setObject(String key, Object obj) {

        Jedis jedis = null;
        try {
            jedis = pool.getResource();

            jedis.set(key, JSON.toJSONString(obj));
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }

    public <T> T getObject(String key, Class<T> t) {

        Jedis jedis = null;
        try {
            jedis = pool.getResource();

            String v = jedis.get(key);
            return JSON.parseObject(v, t);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);

        } finally {
            close(jedis);
        }
        return null;
    }

    public <T> T getObject(String key, TypeReference<T> type) {

        Jedis jedis = null;
        try {
            jedis = pool.getResource();

            String v = jedis.get(key);
            return JSON.parseObject(v, type);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);

        } finally {
            close(jedis);
        }
        return null;
    }

    /**
     * 根据key获取一个String
     *
     * @param key
     * @return
     */
    public String get(String key) {

        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            String val = jedis.get(key);

            return val;
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return null;
    }

    public String flushAll() {

        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.flushAll();
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return null;
    }

    /**
     * 保存value到redis中
     *
     * @param key
     * @param value
     */
    public void setString(String key, String value) {

        if (value == null) {
            return;
        }
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.set(key, value);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }


    public void setObject(String key, Object obj, int expirySeconds) {

        if (obj == null) {
            return;
        }
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.setex(key, expirySeconds, JSON.toJSONString(obj));
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }

    /**
     * 保存一个map到redis中
     *
     * @param key
     * @param map
     */
    public void setObject(String key, Map<String, String> map) {

        if (map == null || map.isEmpty()) {
            return;
        }

        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.hmset(key, map);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }

    /**
     * 获取一个map的元素数量
     *
     * @param key
     * @return
     */
    public long hlen(String key) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.hlen(key);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);

        } finally {
            close(jedis);
        }

        return 0;
    }

    /**
     * 获取一个对象中的多个field的值
     *
     * @param key    指定一个对象
     * @param fields 指定map中的多个key
     * @return
     */
    public List<String> getFieldsFromObject(String key, String... fields) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.hmget(key, fields);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);

        } finally {
            close(jedis);
        }

        return null;
    }

    /**
     * 删除一个key
     *
     * @param key
     */
    public void delete(String key) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.del(key);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }

    /**
     * 判断一个key存不存在
     *
     * @param key
     * @return
     */
    public boolean exists(String key) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.exists(key);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }

        return false;
    }

    /**
     * 将value保存到一个对象的字段中
     *
     * @param key
     * @param field
     * @param value
     */
    public void setFieldToObject(String key, String field, String value) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.hset(key, field, value);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }

    public void setFieldsToObject(String key, Map<String, String> hash) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.hmset(key, hash);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }

    public void setFieldsToObject(String key, Map<String, String> hash, int seconds) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.hmset(key, hash);
            jedis.expire(key, seconds);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }

    public void expire(String key, int seconds) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.expire(key, seconds);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }

    /**
     * 从一个对象中删除某个字段
     *
     * @param key
     * @param field
     */
    public void delFieldFromObject(String key, String field) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.hdel(key, field);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }

    /**
     * 获取一个对象的某个字段的值
     *
     * @param key
     * @param field
     * @return
     */
    public String getFieldFromObject(String key, String field) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.hget(key, field);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 获取整个Object
     *
     * @param key
     * @return
     */
    public Map<String, String> getMap(String key) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.hgetAll(key);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);

            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 加锁
     * @param key 锁名
     * @param timeout 单位秒
     */
    public boolean setnx(String key,int timeout){
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            if (jedis.setnx(key, "TRUE") == 1) {
                jedis.expire(key, timeout);
                return true;
            }else{
                jedis.expire(key, timeout);
            }
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return false;
    }


    public boolean lock(String lockKey,long timeout) {
        Jedis jedis = null;
        long nano = System.nanoTime();
        timeout *= 1000000000L;
        try {
            jedis = pool.getResource();
            while ((System.nanoTime() - nano) < timeout) {
                if (jedis.setnx(lockKey, "TRUE") == 1) {
                    jedis.expire(lockKey, 8);
                    return true;
                }
                // 短暂休眠，nano避免出现活锁
                Thread.sleep(3, (new Random()).nextInt(500));
            }
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return false;
    }
    public void unlock(String lockKey){
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.del(lockKey);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }

    /**
     * 内存中的值+x
     *
     * @param key
     * @param incrVal
     * @return
     */
    public long incrBy(String key, long incrVal) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.incrBy(key, incrVal);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return 0;
    }

    /**
     * 向一个队列插入一条消息
     *
     * @param key
     * @param val
     */
    public void pushMsg(String key, String val) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.rpush(key, val);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }

    /**
     * 从一个队列消费一条消息
     *
     * @param key
     * @return
     */
    public String popMsg(String key) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.lpop(key);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 获取list中指定中某些位置的元素
     *
     * @param key
     * @param startIndex
     * @param endIndex
     * @return
     */
    public List<String> getListElements(String key, int startIndex, int endIndex) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.lrange(key, startIndex, endIndex);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return null;
        } finally {
            close(jedis);
        }
    }

    public String blockPopMsg(int timeOut, String... keys) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            List<String> ret = jedis.blpop(timeOut, keys);
            if (ret != null) {
                return ret.get(1);
            }
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return null;
    }

    /**
     * 获取一个
     *
     * @param key
     * @return
     */
    public List<String> getMapValues(String key) {

        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.hvals(key);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);

        } finally {
            close(jedis);
        }
        return null;
    }

    /**
     * 获取一个
     *
     * @param key
     * @return
     */
    public void sadd(String key, String member) {

        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.sadd(key, member);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);

        } finally {
            close(jedis);
        }

    }

    /**
     * 获取一个
     *
     * @param key
     * @return
     */
    public void srem(String key, String member) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.srem(key, member);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }

    /**
     * 获取一个
     *
     * @param key
     * @return
     */
    public boolean sIsMember(String key, String member) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.sismember(key, member);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return false;
    }

    /**
     * 获取一个
     *
     * @param key
     * @return
     */
    public Set<String> smembers(String key) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.smembers(key);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return null;
    }

    /**
     * 求一个集合的元素个数
     *
     * @param key
     * @return
     */
    public long llen(String key) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.llen(key);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return 0;

    }

    public void zadd(String key, double score, String member) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.zadd(key, score, member);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);

        } finally {
            close(jedis);
        }
    }

    public Set<String> zmember(String key, double start, double end) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.zrangeByScore(key, start, end);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return null;
    }

    public Set<Tuple> zRangeWithScores(String key, long start, long end) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.zrangeWithScores(key, start, end);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return null;
    }

    public Double zscore(String key, String member) {

        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.zscore(key, member);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }

        return null;

    }

    public void close(Jedis jedis) {
        if (jedis != null) {
            try {
                jedis.close();
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
            }
        }
    }

    public boolean addMapValueByKey(String redisKey, String mapKey, Object value) {
      /*  Map<String, String> mapStr = JedisHelper.getInstance(redisPath).getMap(redisKey);
        if (mapStr == null)
            mapStr = new HashMap<String, String>();
        mapStr.put(mapKey, JSON.toJSONString(value));
        JedisHelper.getInstance(redisPath).setObject(redisKey, mapStr);
*/
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.hset(redisKey, mapKey, JSON.toJSONString(value));
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }

        return true;
    }

    public boolean removeMapValueByKey(String redisKey, String mapKey) {
        Jedis jedis = null;
        try {
            jedis = JedisHelper.getInstance(redisPath).getJRedis();
            if (jedis != null)
                jedis.clientSetname("RemoveMapData");
            jedis.hdel(redisKey, mapKey);
        } catch (Exception e) {
            LOG.error("Remove Map Value Error!");
            e.printStackTrace();
        } finally {
            JedisHelper.getInstance(redisPath).close(jedis);
        }

        return true;
    }

    public static void main(String[] args) {
        String redisPath = "./Config/redis.properties";
        Jedis jedis = JedisHelper.getInstance(redisPath).getJRedis();
        try {
            if (jedis != null) {
                Pipeline pipeline = jedis.pipelined();
                for (int i = 0; i < 10000; i++) {
                    pipeline.set("ppbbtt" + i, "你真的是好霸气哦哦哦" + i);
                }
                pipeline.sync();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JedisHelper.getInstance(redisPath).close(jedis);
        }
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long start = System.currentTimeMillis();
        System.out.println("系统当前时间: " + start);
        List<String> getValue = getInstance(redisPath).getValues("ppbbtt*");
        for (String value : getValue) {
            System.out.println("Value = " + value);
        }
        System.out.println("花费的时间：" + (System.currentTimeMillis() - start));
        System.out.println(System.currentTimeMillis());
    }


    public Jedis getJRedis() {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis;
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 获取所有匹配的字符串的返回值
     *
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            return jedis.keys(pattern + "*");
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return null;
    }

    /**
     * 获取所有匹配的字符串的Key对应的值
     *
     * @param pattern
     * @return
     */
    public List<String> getValues(String pattern) {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            Set<String> keys = jedis.keys(pattern + "*");
            if (keys == null || keys.isEmpty())
                return null;
            String[] keyArr = new String[keys.size()];
            keyArr = keys.toArray(keyArr);
            return jedis.mget(keyArr);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return null;
    }


    public <T> T executeWithLock(Jedis jedis, IRedisLogic<T> logic, String... keys) {
        long enter = System.currentTimeMillis();
        boolean close = true;
        if (jedis == null) {
            jedis = getJRedis();
            if (jedis != null)
                jedis.clientSetname("ExecuteWithLock");
        } else {
            close = false;
        }

        try {
            while (System.currentTimeMillis() - enter < 5000) {
                jedis.watch(keys);
                logic.getDataFromJedis(jedis);

                Transaction transaction = jedis.multi();
                T t = logic.execute(transaction);
                if (transaction.exec() != null) {
                    return t;
                }

                Thread.sleep(50);
            }
        } catch (Exception e) {
            LOG.error("executeWithLock", e);
        } finally {
            if (jedis != null) {
                jedis.unwatch();

                if (close) {
                    jedis.close();
                }
            }
        }
        return null;
    }

    public long publish(String subject, String message){
        Jedis jedis = null;
        try{
            jedis = pool.getResource();
            return jedis.publish(subject, message);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return 0;
    }

    public void subscribe(JedisPubSub pubSub, String... subject){
        Jedis jedis = null;
        try{
            jedis = pool.getResource();
            jedis.subscribe(pubSub, subject);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }
}
